package ga

import (
	"fmt"
	"math/rand"
)

//染色体接口
type ChromosomeInterface interface {
	//设置适应度函数
	SetFitnessFunc(f GetFitnessHandler)
	//获取基因值
	GetGene(offset int64) float64
	//获取适应度
	GetFitness() float64
	//打印详情
	Dump()
	//获取染色体长度
	GetLength() int64
	//染色体交叉
	Crossover(ChromosomeInterface) ChromosomeInterface
	//染色体变异
	Mutation(r float64)
}

type GetFitnessHandler func(c *Chromosome) float64
type DumpFunc func(data interface{})

type Chromosome struct {
	DataArray       []float64
	IsFitnessCached bool
	FitnessCached   float64
	getFitnessFunc  GetFitnessHandler
}

func NewChromosome(data []float64, len int64) ChromosomeInterface {
	return &Chromosome{
		DataArray: data,
	}
}

func NewRandChromosome(length int64, min, max float64) ChromosomeInterface {
	var data []float64
	for i := int64(0); i < length; i++ {
		one := rand.Float64()*(max-min) + min
		data = append(data, one)
	}
	return &Chromosome{
		DataArray:       data,
		IsFitnessCached: false,
	}
}

func BuildEmptyChromosome(length int64) ChromosomeInterface {
	return &Chromosome{
		DataArray: make([]float64, 0, length),
	}
}

func CopyChromosome(data *Chromosome) ChromosomeInterface {
	var result Chromosome
	result.IsFitnessCached = data.IsFitnessCached
	result.FitnessCached = data.FitnessCached
	copy(result.DataArray, data.DataArray)
	return &result
}

func (c *Chromosome) SetGene(offset int64, value float64) bool {
	if int(offset) > len(c.DataArray)-1 {
		return false
	}
	if c.DataArray[offset] != value {
		c.DataArray[offset] = value
		c.IsFitnessCached = false
	}
	return true
}

func (c *Chromosome) SetFitnessFunc(f GetFitnessHandler) {
	c.getFitnessFunc = f
}

func (c *Chromosome) GetGene(offset int64) float64 {
	if int(offset) > len(c.DataArray)-1 {
		return 0
	}
	return c.DataArray[offset]
}

func (c *Chromosome) GetFitness() float64 {
	if c.IsFitnessCached {
		return c.FitnessCached
	}
	if c.getFitnessFunc == nil {
		fmt.Println("func err")
		return 0
	}
	c.FitnessCached = c.getFitnessFunc(c)
	c.IsFitnessCached = true
	return c.FitnessCached
}

func (c *Chromosome) Dump() {
	fmt.Println("Fitness=", c.GetFitness(), "v1=", c.DataArray[0], "v2=", c.DataArray[1], "v3=", c.DataArray[2])
}

func (c *Chromosome) GetLength() int64 {
	return int64(len(c.DataArray))
}

func (c *Chromosome) Crossover(another ChromosomeInterface) (result ChromosomeInterface) {
	if another.GetLength() != int64(len(c.DataArray)) {
		return nil
	}
	array := make([]float64, len(c.DataArray))
	for i := 0; i < len(c.DataArray); i++ {
		array[i] = (c.DataArray[i] + another.GetGene(int64(i))) / 2
	}
	result = NewChromosome(array, int64(len(c.DataArray)))
	result.SetFitnessFunc(c.getFitnessFunc)
	return
}

func (c *Chromosome) Mutation(r float64) {
	if r < 0.0 {
		return
	}
	//rand.Seed(time.Now().Unix())
	for i := int64(0); i < int64(len(c.DataArray)); i++ {
		c.DataArray[i] += rand.NormFloat64() * r
	}
	c.IsFitnessCached = false
}
